package edu.hznu.retroSnakerPlanServer.controller;

import edu.hznu.retroSnakerPlanServer.dto.RequestMsg;
import edu.hznu.retroSnakerPlanServer.dto.ResponseMsg;
import edu.hznu.retroSnakerPlanServer.entity.*;
import edu.hznu.retroSnakerPlanServer.factory.GameContextFactory;
import edu.hznu.retroSnakerPlanServer.service.FoodService;
import edu.hznu.retroSnakerPlanServer.service.PlayerService;
import edu.hznu.retroSnakerPlanServer.utils.ResponseEncode;
import edu.hznu.retroSnakerPlanServer.service.imp.FoodServiceImpl;
import edu.hznu.retroSnakerPlanServer.service.imp.PlayerServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.LinkedList;
import java.util.List;

/**
 * create by Cliven on 2018-06-01 19:04
 *
 * @author Cliven
 * 请求处理控制器
 */
public class ProcessController {

    private static final Logger log = LoggerFactory.getLogger(ProcessController.class);
    private GameContext ctx;
    private FoodService foodService;
    private PlayerService playerService;

    /**
     * 构造处理控制器
     */
    public ProcessController(GameContext... gameContexts) {
        if (gameContexts != null && gameContexts.length > 0 && gameContexts[0] != null) {
            this.ctx = gameContexts[0];
        } else {
            this.ctx = GameContextFactory.generate();
        }
        this.foodService = new FoodServiceImpl(ctx);
        this.playerService = new PlayerServiceImpl(ctx);
    }

    /**
     * 根据请求类型调用对应的处理控制器
     *
     * @param requestMsg 请求实体
     * @return 响应实体字节串
     * @author Cliven
     * @since 2018-06-01 21:26:23
     */
    public Byte[] process(RequestMsg requestMsg) {

        log.debug(requestMsg.toString());

        Byte[] res = null;
        Integer reqType = requestMsg.getReqType();
        if (ReqType.NO_ID.equals(reqType)) {
            res = createPlayer();
        } else if (ReqType.UPDATE.equals(reqType)) {
            res = updatePlayer(requestMsg);
        } else if (ReqType.DEAD.equals(reqType)) {
            res = killPlayer(requestMsg);
        } else {
            throw new IllegalArgumentException("Process requestMsg get unknown request type");
        }
        for (Player player : ctx.getPlayers().values()) {
            log.debug(player.getSnakeNodes().toString());
        }

        return res;
    }

    /**
     * 创建玩家
     *
     * @return 区域内容实体
     * @author Cliven
     * @since 2018-06-01 20:48:04
     */
    public Byte[] createPlayer() {
        Player player = playerService.create();
        log.info("Create new player: {}", player.toString());
        ResponseMsg responseMsg = getAreaInfo(player.getId(), ReqType.NO_ID);

        return ResponseEncode.encode(responseMsg);
    }

    /**
     * 更新玩家信息
     *
     * @param requestMsg 请求实体
     * @return 区域内容实体
     * @author Cliven
     * @since 2018-06-01 21:02:36
     */
    public Byte[] updatePlayer(RequestMsg requestMsg) {
        if (requestMsg == null) {
            throw new IllegalArgumentException("Update player info requestMsg is null");
        }
        // 通过ID查找玩家
        Integer id = requestMsg.getId();
        Player player = playerService.findById(id);
        if (player == null) {
            throw new IllegalArgumentException("Update player info unknown player id");
        }


        boolean cntSetSuccess = msgCntProcess(player, requestMsg.getMsgCnt());
        // 如果设置失败就直接响应空
        if (!cntSetSuccess) {
            return null;
        }
        // 更新玩家信息
        List<SnakeNode> snakeNodes = requestMsg.getPlayerInfo();
        // 拒绝空值的蛇节点更新
        if (snakeNodes != null && snakeNodes.size() > 0) {
            player.setSnakeNodes(requestMsg.getPlayerInfo());
            playerService.update(player);
        }

        // 移除吃掉的食物信息
        foodService.removeFoods(requestMsg.getEats());

        // 整合信息
        ResponseMsg responseMsg = getAreaInfo(player.getId(), requestMsg.getReqType());
        return ResponseEncode.encode(responseMsg);
    }

    /**
     * 设置消息计数器
     *
     * @param player 要设置的玩家
     * @param cnt    设置值
     * @return 设置结果成功/失败
     * @author Cliven
     * @since 2018-06-03 16:00:02
     */
    private synchronized boolean msgCntProcess(Player player, Integer cnt) {
        boolean success = false;
        if (cnt == 0) {
            player.setMsgCnt(0);
            success = true;
        } else if (player.getMsgCnt() <= cnt) {
            player.setMsgCnt(cnt);
            success = true;
        }
        return success;
    }

    /**
     * 击杀玩家
     *
     * @param requestMsg 请求实体
     * @return 被杀玩家信息实体字节串
     * @author Cliven
     * @since 2018-06-01 21:12:59
     */
    public Byte[] killPlayer(RequestMsg requestMsg) {
        if (requestMsg == null) {
            throw new IllegalArgumentException("Kill player requestMsg is null");
        }
        Integer id = requestMsg.getId();
        Integer killerId = requestMsg.getKiller();
        // 移除被杀的玩家玩家
        Player beKilled = playerService.remove(id);
        // 把身体变为食物
        foodService.add(requestMsg.getPlayerInfo());

        // 如果玩家不是幢墙则相应的跟新其他玩家分数
        if (killerId != 0) {
            Player killer = playerService.findById(killerId);
            if (killer != null) {
                killer.addScore();
                playerService.update(killer);
                log.info("Remove a player: {}", beKilled.toString());
            }
        }
        ResponseMsg responseMsg = new ResponseMsg()
                .setId(id)
                .setScore(beKilled.getScore())
                .setResType(requestMsg.getReqType());
        return ResponseEncode.encode(responseMsg);
    }

    /**
     * 根据玩家ID获取玩家周围固定区域的信息
     * 信息包括：玩家信息、食物信息
     *
     * @param id      玩家ID
     * @param reqType 响应类型
     * @return 响应实体
     * @author Cliven
     * @since 2018-6-1 20:39:52
     */
    public ResponseMsg getAreaInfo(Integer id, Integer reqType) {
        Player player = playerService.findById(id);
        if (player == null) {
            throw new IllegalArgumentException("Get area Info unknown player id");
        }
        SnakeNode head = player.getSnakeNodes().get(0);
        Integer x = head.getX();
        Integer y = head.getY();

        // 区域构造
        Area area = new Area();
        area.sx = (x - ctx.getSearchRadius() < 0) ? 0 : x - ctx.getSearchRadius();
        area.sy = (y - ctx.getSearchRadius() < 0) ? 0 : y - ctx.getSearchRadius();
        area.ex = (x + ctx.getSearchRadius() > ctx.getWidth()) ? ctx.getWidth() : x + ctx.getSearchRadius();
        area.ey = (y + ctx.getSearchRadius() > ctx.getHeight()) ? ctx.getHeight() : y + ctx.getSearchRadius();
        log.debug("Area: x: [{}, {}] y: [{}, {}]", area.sx, area.ex, area.sy, area.ey);

        // 根据区域获取玩家信息
        List<Player> areaPlayers = playerService.getAreaPlayer(area);

        // 获取区域玩家中的信息
        List<List<SnakeNode>> snakes = new LinkedList<>();
        for (Player playerItem : areaPlayers) {
            snakes.add(playerItem.getSnakeNodes());
        }
        log.debug("snakes:{}", snakes.toString());

        // 获取区域内的所有食物
        List<Food> foods = foodService.getFoodsByArea(area);
        log.info("foods:{}", foods.toString());

        ResponseMsg responseMsg = new ResponseMsg()
                .setResType(reqType)
                .setMsgCnt(player.getMsgCnt())
                .setId(id)
                .setScore(player.getScore())
                .setPlayersInfo(snakes)
                .setFoods(foods);

        log.debug(responseMsg.toString());
        return responseMsg;
    }


}
